using System.Data;
using MbUnit.Framework;
using Moq;

namespace Mite.Core.Tests.Unit
{
	public abstract class AdapterTestFixtureBase
	{
		protected abstract Adapter GetAdapter();

		[Test]
		public void CreateIndex_Uses_QueryFactory()
		{
			const string indexName = "IX_Test";
			const string createReturn = "test";
			const string tableName = "some_table";

			var index = new Index(indexName, new Table(tableName));

			var queryFactory = new Mock<ISqlQueryFactory>();
			queryFactory
				.Expect(
				x => x.CreateIndex(It.Is<Index>(arg => arg.Equals(index))))
				.Returns(createReturn).AtMostOnce().Verifiable();

			Adapter adapter = GetAdapter();
			adapter.QueryFactory = queryFactory.Object;

			IDbConnection conn = CreateConnectionMock();

			IDbCommand cmd = adapter.CreateIndex(index, conn);

			Assert.AreEqual(createReturn, cmd.CommandText);
			Assert.AreEqual(conn, cmd.Connection);

			queryFactory.Verify();
		}

		[Test]
		public void DropIndex_Uses_QueryFactory()
		{
			const string indexName = "IX_TEST";
			const string returns = "test";
			const string tableName = "some_table";

			var index = new Index(indexName, new Table(tableName), true);

			var queryFactoryMock = new Mock<ISqlQueryFactory>();
			queryFactoryMock.Expect(x => x.DropIndex(It.Is<Index>(arg => arg.Equals(index)))).Returns(returns).AtMostOnce().
				Verifiable();

			IDbConnection conn = CreateConnectionMock();

			Adapter adapter = GetAdapter();
			adapter.QueryFactory = queryFactoryMock.Object;
			IDbCommand cmd = adapter.DropIndex(index, conn);

			Assert.AreEqual(returns, cmd.CommandText);
			Assert.AreEqual(conn, cmd.Connection);

			queryFactoryMock.Verify();
		}

		[Test]
		public void CreateTable_Uses_QueryFactory()
		{
			const string tableName = "some_table";
			const string returns = "returns";

			var idColumn = new MigrationBase.Int32("Id", ColumnAttributes.PrimaryKey | ColumnAttributes.Identity, null);

			var table = new Table(tableName, idColumn);

			var queryFactoryMock = new Mock<ISqlQueryFactory>();
			queryFactoryMock.Expect(x => x.CreateTable(It.Is<Table>(tbl => tbl == table))).Returns(returns).AtMostOnce().
				Verifiable();

			IDbConnection conn = CreateConnectionMock();

			Adapter adapter = GetAdapter();
			adapter.QueryFactory = queryFactoryMock.Object;

			IDbCommand cmd = adapter.CreateTable(conn, table);

			Assert.AreEqual(returns, cmd.CommandText);
			Assert.AreEqual(conn, cmd.Connection);
		}

		[Test]
		public void DropTable_Uses_QueryFactory()
		{
			const string tableName = "some_table";
			const string returns = "returns";

			var table = new Table(tableName);

			var queryFactoryMock = new Mock<ISqlQueryFactory>();
			queryFactoryMock.Expect(x => x.DropTable(It.Is<Table>(tbl => tbl == table))).Returns(returns).AtMostOnce().Verifiable
				();

			IDbConnection conn = CreateConnectionMock();

			Adapter adapter = GetAdapter();
			adapter.QueryFactory = queryFactoryMock.Object;
			IDbCommand cmd = adapter.DropTable(conn, table);

			Assert.AreEqual(returns, cmd.CommandText);
			Assert.AreEqual(conn, cmd.Connection);

			queryFactoryMock.Verify();
		}

		[Test]
		public void RenameTable_Uses_QueryFactory()
		{
			const string returns = "returns";
			const string newName = "new_table_name";

			Table table = GetTestTable();
			IDbConnection conn = CreateConnectionMock();
			Adapter adapter = GetAdapter();

			var queryFactoryMock = new Mock<ISqlQueryFactory>();
			queryFactoryMock.Expect(x => x.RenameTable(It.Is<Table>(tbl => tbl == table), It.Is<string>(s => s == newName))).
				Returns(returns).AtMostOnce().Verifiable();

			adapter.QueryFactory = queryFactoryMock.Object;

			IDbCommand cmd = adapter.RenameTable(conn, table, newName);

			Assert.AreEqual(returns, cmd.CommandText);
			Assert.AreEqual(conn, cmd.Connection);

			queryFactoryMock.Verify();
		}

		[Test]
		public void RenameColumn_Uses_QueryFactory()
		{
			const string returns = "returns";
			const string oldName = "name";
			const string newName = "new_column_name";

			Column columnToRename = new MigrationBase.String(oldName);

			Table table = GetTestTable(columnToRename);
			IDbConnection conn = CreateConnectionMock();
			Adapter adapter = GetAdapter();

			var queryFactoryMock = new Mock<ISqlQueryFactory>();
			queryFactoryMock.Expect(
				x => x.RenameColumn(It.Is<Column>(arg => arg == columnToRename), It.Is<string>(arg => arg == newName))).Returns(
				returns).AtMostOnce().Verifiable();

			adapter.QueryFactory = queryFactoryMock.Object;

			IDbCommand cmd = adapter.RenameColumn(conn, columnToRename, newName);

			Assert.AreEqual(returns, cmd.CommandText);
			Assert.AreEqual(conn, cmd.Connection);

			queryFactoryMock.Verify();
		}

		[Test]
		public void AlterColumn_Uses_QueryFactory()
		{
			const string returns = "returns";
			const string columnName = "column";

			var originalColumn = new Column(columnName, DbType.String) { Length = 200 };
			var newColumn = new Column(columnName, DbType.String, ColumnAttributes.NotNull) { Length = 255 };

			Table table = GetTestTable(originalColumn);
			IDbConnection conn = CreateConnectionMock();
			Adapter adapter = GetAdapter();

			newColumn.Table = table;

			var queryFactoryMock = new Mock<ISqlQueryFactory>();
			queryFactoryMock.Expect(
				x => x.AlterColumn(It.Is<Column>(arg => arg == originalColumn), It.Is<Column>(arg => arg == newColumn))).Returns(
				returns).AtMostOnce().Verifiable();

			adapter.QueryFactory = queryFactoryMock.Object;

			IDbCommand cmd = adapter.AlterColumn(conn, originalColumn, newColumn);

			Assert.AreEqual(returns, cmd.CommandText);
			Assert.AreEqual(conn, cmd.Connection);

			queryFactoryMock.Verify();
		}

		protected static IDbConnection CreateConnectionMock()
		{
			var connectionMock = new Mock<IDbConnection>();

			var cmdMock = new Mock<IDbCommand>();
			cmdMock.Stub(x => x.CommandText);
			cmdMock.Expect(x => x.Connection).Returns(connectionMock.Object);

			connectionMock.Expect(x => x.CreateCommand()).Returns(cmdMock.Object);

			return connectionMock.Object;
		}

		protected static Table GetTestTable(params Column[] columns)
		{
			const string tableName = "some_table";

			return new Table(tableName, columns);
		}
	}
}